<?php

define('BASEDIR',__DIR__);

include BASEDIR . '/IMooc/Loader.php';
spl_autoload_register('\\IMooc\\Loader::autoload');//注册自动加载

//App\Controller\Home\Index::test();
//Test\DataStruct::stackTest();
//Test\DataStruct::queueTest();
//Test\DataStruct::minHeapTest();
//Test\DataStruct::fixedArrayTest();

//**********工厂模式 start**********************
//不使用工厂模式new Database
$db = new IMooc\Database();
//使用工厂模式new Database
$db = IMooc\Factory::createDatabase();
//好处是若Database发生了一些变更，比如修改了名字，或者参数发生了变化
//那么不使用工厂模式则需要每一处都修改new Database，若使用工厂模式则
//只需要在Factory::createDatabase中使用即可
//工厂模式也是很多高级设计模式的基础
//**********工厂模式 end**********************


//**********单例设计模式 start**********************
//比如需要建立数据库的连接，那么无论创建多少个对象，都
//只连接了一次数据库，节省了很多的资源
$db = IMooc\DatabaseInstance::getInstance();
$db = IMooc\DatabaseInstance::getInstance();
$db = IMooc\DatabaseInstance::getInstance();
//**********单例设计模式 end**********************


//**********注册器模式 start********************
IMooc\Factory::createDatabase();//注册db类
$db = \IMooc\Register::get('db1');
//**********注册器模式 end**********************


//**********适配器模式 start********************
/*
1、适配器模式，可以将截然不同的函数接口封装成统一的API
2、实际应用举例，PHP的数据库操作有mysql、mysqli、pdo三种，
可以用适配器模式统一成一致。类似的场景还有cache适配器，将
memcache，redis，file，apc等不同的缓存函数，统一成一致
 */
//下方三个类实现了适配器接口IDatabase，因此，可以在任意的
//三种数据库操作之间进行切换（只需要改new的类名）其他的代
//码一行不用改。
//比如在一个框架里面要支持所有的环境，有些人的电脑上没有
//MySQL扩展，那么就可以根据使用者的扩展自动选择任意一种支
//持的扩展进行适配
//$db = new IMooc\Database\MySQL();
//$db->connect('127.0.0.1', 'root', 'root', 'testDatabase');
//$db->query('show tables');
//$db->close();

$db = new IMooc\Database\MySQLi();
$db->connect('127.0.0.1', 'root', 'root', 'test');
$db->query('show tables');
$db->close();

$db = new IMooc\Database\PDO();
$db->connect('127.0.0.1', 'root', 'root', 'test');
$db->query('show tables');
$db->close();
//**********适配器模式 end**********************

//**********策略模式 start********************
/*
1.策略模式，将一组特定的行为和算法封装成类，以适应某些
特定的上下文环境，这种模式就是策略模式
2.实际应用举例，例如一个电商网站系统，针对男性、女性用户要
各自跳转到不同的商品类目，并且所有广告位展示不同广告
3.使用策略模式可以实现Ioc、依赖倒置、控制反转
传统的做法是
    if( 性别==男)...
    elseif( 性别==女)...
这是属于硬编码的形式，比如说某一天要增加一种新的类型判断，那么所有if...else的
地方都需要进行修改，如果使用了策略模式之后，它与上下文的环境是中立的，互相不依赖
比如上方的男女用户的广告，只需要增加一种策略即可，其他的所有的地方只需要应用不同
的策略就可以解决到处加条件判断的问题
*/

//传统硬编码方式
class Page
{
    function index(){
        if(isset($_GET['female'])){

        }elseif(isset($_GET['male'])){

        }
    }
    function details(){
        if(isset($_GET['female'])){

        }elseif(isset($_GET['male'])){

        }
    }
}
$page = new Page();
$page->index();

//策略模式
class Page2{
    protected $strategy;
    function index(){
        $this->strategy->showAd();
        $this->strategy->showCategory();
    }
    function details(){
        $this->strategy->showAd();
        $this->strategy->showCategory();
    }
    function setStrategy(\IMooc\UserStrategy $strategy){
        $this->strategy = $strategy;
    }
}
$page = new Page2();
if(isset($_GET['female'])){
    $strategy = new \IMooc\FemaleUserStrategy();
}else{
    $strategy = new \IMooc\MaleUserStrategy();
}/*elseif(1){
    //新增的策略，只需要新增一个策略类即可
}*/
$page->setStrategy($strategy);
$page->index();
//**********策略模式 end**********************

//**********数据对象隐射模式 start**********************
//1、数据对象映射模式，是将对象和数据存储映射起来，对一个
//对象的操作会映射为对数据存储的操作
//2、在代码中实现数据对象映射模式，我们将实现一个ORM类，将
//复杂的SQL语句映射成对象属性的操作
//3、结合使用数据对象映射模式，工厂模式，注册模式

//将类的属性与表的字段映射起来
//因为在析构方法中有修改数据的sql操作，所以程序运行完就会直接修改数据
$user = new IMooc\User(1);
$user->name = 'xiaoming';
$user->mobile = "13000000000";
$user->regtime = date('Y-m-d H:i:s');

//复杂注册器模式例子
class Page3
{
    function index()
    {
        $user = IMooc\Factory::getUser(1);
        $user->name = 'rango';
        $this->test();
    }
    function test()
    {
        $user = IMooc\Factory::getUser(1);
        $user->mobile = '18933334444';
    }
}
$obj = new Page3();
$obj->index();
//**********数据对象隐射模式 end**********************


//**********观察者模式 start**********************
/*
 1、观察者模式（Observer），当一个对象状态发生改变时，依赖
它的对象全部会收到通知，并自动更新
2、场景：一个事件发生后，要执行一连串更新操作。传统的编程方式，
就是在时间的代码之后直接加入处理逻辑。当更新的逻辑增多之后，
代码会变得难以维护。这种方式是耦合的，侵入式的，增加新的逻辑
需要修改事件主体的代码
3、观察者模式实现了低耦合，非侵入式的通知与更新机制
 */
//传统编程方式
//这种方式是耦合，侵入式的，如果需要增加新的逻辑需要修改事件主体的代码
class EventHard
{
    function trigger()
    {
        echo "Event （传统编程方式） <br/>\n";
        //update 操作
        echo "逻辑1（传统编程方式） <br/>\n";
        echo "逻辑2（传统编程方式）  <br/>\n";
        echo "逻辑3（传统编程方式）  <br/>\n";
    }
}
$event = new EventHard();
$event->trigger();

//观察者模式
class Event extends \IMooc\EventGenerator
{
    function trigger()
    {
        echo "Event（观察者模式） <br/>\n";//假设事件发生了
        $this->notify();
    }
}
//定义观察者1
class Observer1 implements \IMooc\Observer
{
    function update($event_info = null)
    {
        echo "逻辑1 （观察者1）<br/>\n";
    }
}
//定义观察者2
class Observer2 implements \IMooc\Observer
{
    function update($event_info = null)
    {
        echo "逻辑2 （观察者2）<br/>\n";
    }
}
//定义观察者3
class Observer3 implements \IMooc\Observer
{
    function update($event_info = null)
    {
        echo "逻辑3 （观察者3）<br/>\n";
    }
}
$event = new Event;
$event->addObserver( new Observer1());//增加一个观察者
$event->addObserver( new Observer2());//增加一个观察者
$event->addObserver( new Observer3());//增加一个观察者
$event->trigger();
//**********观察者模式 end************************


//**********原型模式 start************************
/*
1、原型模式是属于创建型设计模式，跟工厂模式作用类似
2、与工厂模式的实现不同，原型模式是先创建好一个原型对象，
然后通过clone原型对象来创建新的对象。这样就免去了类创建时
重复的初始化操作
3、原型模式适用于大对象的创建。创建一个大对象需要很大的开销，
如果每次都new就会消耗很大，原型模式仅需内存拷贝即可
 */
//传统直接new的方式
echo "-------------传统直接new的方式----------------<br/>";
$canvas1 = new IMooc\Canvas();
$canvas1->init(); //初始化需要循环200次
$canvas1->rect(1, 3, 2, 6);
$canvas1->draw();
echo "----------------------------------------<br/>";
$canvas2 = new IMooc\Canvas();
$canvas2->init(); //初始化需要循环200次
$canvas2->rect(2, 2, 2, 6);
$canvas2->draw();
echo "--------------原型模式----------------------<br/>";
//适用原型模式
$canvas3 = new IMooc\Canvas();
$canvas3->init();
$canvas3->rect(1,3,2,6);
$canvas3->draw();
echo "----------------------------------------<br/>";
$canvas4 = clone $canvas3;//只需要new一次、调用init一次，后续直接使用clone，不需要其他的操作了
$canvas4->rect(2, 2, 2, 6);
$canvas2->draw();
//**********原型模式 end**************************

//**********装饰器模式 start************************
/*
1、装饰器模式（Docorator），可以动态地添加修改类的功能
2、一个类提供了一项功能，如果要在修改并添加额外的功能，
传统的编程模式，需要写一个子类继承它，并重新实现类的方法
3、使用装饰器模式，仅需在运行时添加一个装饰器对象即可实现，
可以实现最大的灵活性
 */
$canv = new IMooc\Canvas();
$canv->init();
$canv->addDecorator(new \IMooc\ColorDrawDecorator('green'));//添加颜色装饰器
$canv->addDecorator(new \IMooc\SizeDrawDecorator('30px'));//添加字体装饰器
$canv->rect(3,6,4,12);
$canv->draw();
//**********装饰器模式 end**************************

//**********迭代器模式 end**************************
/*
 1、迭代器模式，在不需要了解内部实现的前提下，遍历一个聚合
 对象的内部元素。
 2、相比于传统的编程模式，迭代器模式可以隐藏遍历元素的所需的操作
 */
$users = new \IMooc\AllUser();
foreach($users as $user){
    var_dump($user->name);
    $user->name = '666';
}
//**********迭代器模式 end**************************

//**********代理模式 start**************************
/*
1、在客户端与实体之间建立一个代理对象（proxy），客户端对
实体进行的所有操作全部委派给代理对象，隐藏实体的具体实现细节。
2、Proxy还可以与业务代码分离，部署到另外的服务器。业务代码
中通过RPC来委派任务。
有一个实例从user表中读取信息然后修改user的一个字段。在传统编程中
业务代码是需要知道这个信息存储在哪个数据库的哪个表中。
使用代理模式之后，就不再需要关心具体实体的存储位置。可以是存储在数据
库中也可以是存储在其他的位置。
业务代码将所有操作委派给proxy进行操作，而不是在业务代码中进行操作。
典型的应用场景就是MySQL的主从结构。
使用代理模式之后，业务代码不需要做任何修改，就可以实现一个读写分离。
在proxy对象中对所有读的操作去请求从库，对所有写的操作去请求主库。
 */
//实现举例
$proxy = new \IMooc\Proxy();
$proxy->getUserName($id);
$proxy->setUserName($id, 'testUserName');
//**********代理模式 end**************************

/*
面相对象编程的基本原则：
1、单一职责：一个类，只需要做好一件事情。
    不要使用一个类来完成一个很复杂的功能而是拆分设计成更小更具体的类。

2、开发封闭原则：一个类，应该是可扩展的，而不可修改的。
    一个类实现之后对扩展应该是开发的，而对修改应该是封闭的。
    不应该使用修改来增加功能，而是通过扩展来增加功能。

3、依赖倒置：一个类，不应该强依赖另外一个类。每个类对于另外一个类都是可替换的。
    比如A、B两个类，假设A需要依赖B类，那么不应该直接在A类中直接调用B类，而是应该
    使用依赖注入的方式，通过注入将B类的对象注入给A类，这样B对于A来说就是可以替换的。
    如果此时有第三个类C，提供了跟B一致的接口，这时候就可以在C和B之间进行切换。

4、配置化：尽可能地使用配置，而不是硬编码。
    像编程中经常会有一些数据参数还有常量，这些全部应该放入到配置文件中，而不是硬
    编码在代码中。
    还有像类的关系定义，也应该是可以配置的。比如像上一条原则中的A、B、C三个类，那么
    究竟是应该选择B类还是C类，我们不应该直接在代码中去写，而是由配置来决定

5、面向接口编程：只需要关心接口，不需要关心实现。
    所有代码只需要关心某个类提供了哪些接口，不需要关心这个类的具体实现。
    比如第三个原则例子中，A类只需要了解B或者C类提供了某一个接口，而不需要关心
    B、C这两个类具体是怎么样实现的。
*/